
#pragma once

#include "stdafx.h"


class VertexBuffer
{
public:
	VertexBuffer( )
		: m_pVertexBuffer(NULL)
		, m_pIndexBuffer(NULL)
		, m_uVertStride(0)
		, m_uIndexFormat(DXGI_FORMAT_UNKNOWN)
		, m_uBufferTopology(D3D10_PRIMITIVE_TOPOLOGY_UNDEFINED)
		, m_pActualVertBuffer(NULL)
		, m_pActualIndexBuffer(NULL)
		, m_uMaxVertBytes(0)
		, m_uMaxIndexBytes(0)
	{}

	void Clean ( )
	{
		if( m_pVertexBuffer )
		{
			m_pVertexBuffer->Release();
			m_pVertexBuffer = NULL;
		}
		if( m_pIndexBuffer )
		{
			m_pIndexBuffer->Release();
			m_pIndexBuffer = NULL;
		}

		if( m_pActualVertBuffer )
		{
			free( m_pActualVertBuffer );
			m_pActualVertBuffer = NULL;
		}
		if( m_pActualIndexBuffer )
		{
			free( m_pActualIndexBuffer );
			m_pActualIndexBuffer = NULL;
		}
	}

	HRESULT Init ( ID3D10Device & rDevice, u32 uVertStride, u32 uVertCount, u32 uIndexStride, u32 uIndexCount, DXGI_FORMAT uIndexFormat, D3D_PRIMITIVE_TOPOLOGY uBufferTopology )
	{
		m_uVertStride = uVertStride;
		m_uIndexStride = uIndexStride;

		m_uIndexFormat = uIndexFormat;
		m_uBufferTopology = uBufferTopology;

		m_uMaxVertBytes = uVertStride*uVertCount;
		m_uMaxIndexBytes = uIndexStride*uIndexCount;

		//////////////////////////////////////////////////////////////////////////
		// Create Actual Buffers.

		// Verts
		m_pActualVertBuffer = malloc(m_uMaxVertBytes);
		memset( m_pActualVertBuffer, 0, m_uMaxVertBytes );

		// Indices
		m_pActualIndexBuffer = malloc(m_uMaxIndexBytes);
		memset( m_pActualIndexBuffer, 0, m_uMaxIndexBytes );


		//////////////////////////////////////////////////////////////////////////
		// Create the D3D Buffers.
		D3D10_BUFFER_DESC bufferDesc;
		D3D10_SUBRESOURCE_DATA data;
		HRESULT hResult;

		// Verts
		bufferDesc.Usage = D3D10_USAGE_DYNAMIC;
		bufferDesc.ByteWidth = m_uMaxVertBytes;
		bufferDesc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
		bufferDesc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
		bufferDesc.MiscFlags = 0;
		data.pSysMem = m_pActualVertBuffer;

		hResult = rDevice.CreateBuffer( &bufferDesc, &data, &m_pVertexBuffer );
		if( FAILED(hResult) )
			return hResult;


		// Indices
		bufferDesc.Usage = D3D10_USAGE_DYNAMIC;
		bufferDesc.ByteWidth = m_uMaxIndexBytes;        // 36 vertices needed for 12 triangles in a triangle list
		bufferDesc.BindFlags = D3D10_BIND_INDEX_BUFFER;
		bufferDesc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
		bufferDesc.MiscFlags = 0;
		data.pSysMem = m_pActualIndexBuffer;

		hResult = rDevice.CreateBuffer( &bufferDesc, &data, &m_pIndexBuffer );
		if( FAILED(hResult) )
			return hResult;

		Flush();

		return S_OK;
	}

	void AddVert ( void * pVert )
	{
		memcpy( m_pCurrentVert, pVert, m_uVertStride );
		m_pCurrentVert = (void*)(((u8*)m_pCurrentVert)+m_uVertStride);

		u32 uIndexCount = GetIndexCount();
		switch( m_uIndexFormat )
		{
		case DXGI_FORMAT_R32_UINT:
			*((u32*)m_pCurrentIndex) = uIndexCount;
			break;
		default:
			//ERROR
			break;
		}
		m_pCurrentIndex = (void*)(((u8*)m_pCurrentIndex)+m_uIndexStride);
	}

	void Flush ( )
	{
		m_pCurrentVert = m_pActualVertBuffer;
		m_pCurrentIndex = m_pActualIndexBuffer;
	}

	void UpdateBuffers ( ) const
	{
		void * pBufferData = NULL;

		//////////////////////////////////////////////////////////////////////////
		// Lock the vertex buffer.
		HRESULT hResult = m_pVertexBuffer->Map( D3D10_MAP_WRITE_DISCARD, 0, &pBufferData );
		if( FAILED(hResult) )
		{
			// ERROR
			return;
		}

		// Copy the data into the vertex buffer.
		memcpy( pBufferData, (void*)m_pActualVertBuffer, ((u8*)m_pCurrentVert)-((u8*)m_pActualVertBuffer) );

		// Unlock the vertex buffer.
		m_pVertexBuffer->Unmap();


		//////////////////////////////////////////////////////////////////////////
		// Lock the index buffer.
		hResult = m_pIndexBuffer->Map( D3D10_MAP_WRITE_DISCARD, 0, &pBufferData );
		if( FAILED(hResult) )
		{
			// ERROR
			return;
		}

		// Copy the data into the index buffer.
		memcpy( pBufferData, (void*)m_pActualIndexBuffer, ((u8*)m_pCurrentIndex)-((u8*)m_pActualIndexBuffer) );

		// Unlock the vertex buffer.
		m_pIndexBuffer->Unmap();

	}

	// Accessors.
	inline ID3D10Buffer * GetVertexBuffer ( ) const { return m_pVertexBuffer; }
	inline ID3D10Buffer * GetIndexBuffer ( ) const { return m_pIndexBuffer; }

	inline u32 GetVertStride ( ) const { return m_uVertStride; }
	inline DXGI_FORMAT GetIndexFormat ( ) const { return m_uIndexFormat; }
	inline D3D_PRIMITIVE_TOPOLOGY GetBufferTopology ( ) const { return m_uBufferTopology; }

	inline u32 GetVertexCount ( ) const { return (((u8*)m_pCurrentVert)-((u8*)m_pActualVertBuffer))/m_uVertStride; }
	inline u32 GetIndexCount ( ) const { return (((u8*)m_pCurrentIndex)-((u8*)m_pActualIndexBuffer))/m_uIndexStride; }

private:
	// D3D Buffers
	ID3D10Buffer * m_pVertexBuffer;
	ID3D10Buffer * m_pIndexBuffer;

	// Sizing and Format.
	u32 m_uVertStride;
	u32 m_uIndexStride;
	DXGI_FORMAT m_uIndexFormat;
	D3D_PRIMITIVE_TOPOLOGY m_uBufferTopology;
	u32 m_uMaxVertBytes;
	u32 m_uMaxIndexBytes;

	// Actual Buffers.
	void * m_pActualVertBuffer;
	void * m_pActualIndexBuffer;

	// Usage.
	void * m_pCurrentVert;
	void * m_pCurrentIndex;

};